package com.godenwater.recv.server.yanyu;

import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.filterchain.IoFilterAdapter;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.godenwater.yanyu.IMessage;
import com.godenwater.yanyu.IMessageBody;
import com.godenwater.yanyu.IMessageHeader;
import com.godenwater.yanyu.Symbol;
import com.godenwater.yanyu.YYBuilder;
import com.godenwater.yanyu.command.DownCommand;
import com.godenwater.yanyu.utils.ByteUtil;
import com.godenwater.yanyu.utils.CRC16Helper;

public class ServerDataHandler extends IoFilterAdapter implements IoHandler {

	private Logger logger = LoggerFactory.getLogger(this.getClass());

	private final YYServer server;

	private String channel;

	/**
	 * 针对多条消息接收完以后的合并处理，如图片消息，大数据消息
	 */
	private ConcurrentHashMap M3 = new ConcurrentHashMap();

	public ServerDataHandler(YYServer server, String channel) {
		this.server = server;
		this.channel = channel;
	}

	/**
	 * 消息接收后的处理，只处理业务逻辑，此消息处理是在解码后，所以只需要关注消息体的内容
	 */
	public void messageReceived(IoSession session, Object message)
			throws Exception {

		StringBuffer log = new StringBuffer();
		log.append("----------------------------------------------------------------------\n");
		log.append("接收通道" + this.channel + " 服务端已收到 " + server.messageSize()
				+ " 消息数;\n");

		if (message instanceof IoBuffer) {
			System.out.println(">>> HydroServerHandler message is IoBuffer ");
		}

		// 纯粹只处理报文，此消息体已经被解码为单体报文
		IMessage msg = (IMessage) message;

		// 3、应答回复或重发报文请求
		IMessage replyMsg = null;
		IMessageHeader header = msg.getHeader();
		byte[] funcCode = header.getFuncCode();
		byte[] stationAddr = header.getStationAddr();
		String stcd = "" + ByteUtil.bytesToUbyte(stationAddr);

		// 将测站与session关联起来
		// HydroServer.getInstance().getSessionManager().addClient(stcd,
		// session);

		// 1、模式选择，如果是2F表示为M1模式，不需要应答
		if (funcCode[0] != (byte) 0x2F) {// M1模式，无需应答

			// 1、 校验消息
			boolean crcFlag = checkCRC(msg);// 校验消息体，CRC校验

			// 2、 存放于消息队列中，等待处理。将队列放在此处，减少了对链接报的处理
//			
//			ChannelMessage cm = new ChannelMessage();
//			cm.setChannel(channel);
//			cm.setMessage(msg);
//			cm.setCrcFlag(crcFlag);
//			// server.queuePush(cm);
 			//YYServer.getInstance().getMessageManager().append(RecvConstant.YY, msg);

			// mode,ETX（03）表示结束，ETB（17）表示数据未发完。
			if (this.channel.equalsIgnoreCase("GPRS")
					|| this.channel.equalsIgnoreCase("UDP")) {
				if (crcFlag) {
					// 确认应答
					log.append("校验结果，报文CRC验证<相一致>，M2模式发送\"应答\"回复....\n");
					replyMsg = replyMessage(msg);
				} else {
					// 重发请求
					log.append("校验结果，报文CRC验证\"不一致 \"，M2模式发送\"重发\"请求....\n");
					replyMsg = repeatMessage(msg);
				}
			}

			// // M3模式,最后一条报文才进行应答，然后后再考虑重发
			// if (mode == Symbol.SYN
			// && (this.channel.equalsIgnoreCase("GPRS") || this.channel
			// .equalsIgnoreCase("UDP"))) {
			// if (crcFlag) {
			// // 最后一个再确认应答，应答后再检查是否有错误包，有错误包，再发送重发请求
			// log.append("校验结果，报文CRC验证相一致，M3模式发送应答回复....\n");
			// // logger.info(log);
			// // HydroServer.getInstance().getMonitorManager().append(log);
			//
			// // 判断是否为最后一条报文，如果是，才进行回复
			// replyMsg = replyMessage(msg);
			// // 重发错误报文请求
			// // repeatMessage();
			// } else {
			// // 有错误包，需进行记录
			// log.append("校验结果，报文CRC验证不一致，M3模式发送重发请求....\n");
			// // logger.info(log);
			// // HydroServer.getInstance().getMonitorManager().append(log);
			//
			// server.saveErrMessage(msg);
			// }
			// }
		}

		if (replyMsg != null
				&& (this.channel.equalsIgnoreCase("GPRS") || this.channel
						.equalsIgnoreCase("UDP"))) {
			log.append("[回复报文] " + YYBuilder.toHexString(replyMsg, Symbol.DOWN)
					+ "\n");
			session.write(replyMsg);
		}

		logger.info(log.toString());
		// HydroServer.getInstance().getMonitorManager().append(log.toString());
		// 启动重发机制

	}

	/**
	 * 构造HEX回复报文
	 * 
	 * @param msg
	 * @return
	 */
	public IMessage replyMessage(IMessage msg) {
		try {
			IMessageHeader header = msg.getHeader();
			IMessageBody body = msg.getBody();

			// 取出序列值
			byte[] content = body.getContent();

			// 报文回复
			byte[] funcCode = header.getFuncCode();

			DownCommand cmd = new DownCommand();
			cmd.setStartBit(header.getStartBit()[0]);
			cmd.setStationAddr(header.getStationAddr());
			cmd.setCenterAddr(header.getCenterAddr());
			cmd.setFuncCode(funcCode);

			byte mode = msg.getEOF();

			// 1、模式选择，如果是2F，不需要应答

			// 2、判断结束符是哪种格式
			// 2.1 ：在报文分包传输时作为结束符，表示未完成，不可退出通信
			if (msg.getEOF() == Symbol.ETB) {
				// ACK 肯定确认，继续发送，作为有后续报文帧的“确认”结束符。
				// NAK 否定应答，反馈重发， 用于要求对方重发某数据包的报文结束符。
				// ENQ 作为下行查询及控制命令帧的报文结束符。

				cmd.setEof(Symbol.ACK);

			}

			// 2.2：作为报文结束符，表示传输完成等待退出通信，
			if (msg.getEOF() == Symbol.ETX) {
				// --- 反馈用EOT 作为传输结束确认帧报文符，表示可以退出通信。
				// --- ESC 要求终端在线。保持在线10分钟内若没有接收到中心站命令，终端退回原先设定的工作状态
				cmd.setEof(Symbol.EOT);

			}

			return cmd.sendReplyMessage(true);

		} catch (Exception e) {
			e.printStackTrace();
			logger.info(">> 构造回复报文出现异常！" + e.getMessage());
			return null;
		}
	}

	/**
	 * 构造重发报文????有问题未完成
	 * 
	 * @param msg
	 * @return
	 */
	public IMessage repeatMessage(IMessage msg) {

		try {
			IMessageHeader header = msg.getHeader();
			IMessageBody body = msg.getBody();

			byte[] content = body.getContent();
			byte[] funcCode = header.getFuncCode();
			byte mode = msg.getEOF();

			// M2模式，直接发送重发请求
			DownCommand cmd = new DownCommand();
			cmd.setCenterAddr(header.getCenterAddr());
			cmd.setStationAddr(header.getStationAddr());
			cmd.setStartBit(header.getStartBit()[0]);
			cmd.setFuncCode(funcCode);

			cmd.setEof(Symbol.NAK);

			return cmd.sendRepeatMessage();

		} catch (Exception e) {
			e.printStackTrace();
			logger.info(">> 构造重发报文出现异常！" + e.getMessage());
			return null;
		}
	}

	/**
	 * CRC校验
	 * 
	 * @param message
	 * @return
	 */
	public boolean checkCRC(IMessage message) {

		IMessageHeader header = message.getHeader();
		IMessageBody body = message.getBody();

		//logger.info("校验报文， 开始校验CRC...  ");

		byte[] bytes = new byte[header.getLength() + body.getLength() + 1];

		int pos = 0;
		System.arraycopy(header.getStartBit(), 0, bytes, pos,
				header.getStartBit().length);
		pos = pos + header.getStartBit().length; // 此处将取值直接改为取数据中字节的长度，对字节处理更精确

		System.arraycopy(header.getCenterAddr(), 0, bytes, pos,
				header.getCenterAddr().length);
		pos = pos + header.getCenterAddr().length;

		System.arraycopy(header.getStationAddr(), 0, bytes, pos,
				header.getStationAddr().length);
		pos = pos + header.getStationAddr().length;

		System.arraycopy(header.getFuncCode(), 0, bytes, pos,
				header.getFuncCode().length);
		pos = pos + header.getFuncCode().length;

		System.arraycopy(header.getBodySize(), 0, bytes, pos,
				header.getBodySize().length);
		pos = pos + header.getBodySize().length;

		System.out.println(">> " + body.getContent());

		System.arraycopy(body.getContent(), 0, bytes, pos,
				body.getContent().length);
		pos = pos + body.getContent().length;

		bytes[pos] = message.getEOF();

		byte[] crcResult = CRC16Helper.crc16Check(bytes);

		// System.out.println(">> crcResult " +
		// ByteUtil.toHexString(crcResult));

		if (Arrays.equals(crcResult, message.getCRC())) {

			return true;
		} else {
			return false;
		}

	}

	public void viewInfo(IMessage message) {

		System.out.println("******* 中心站查询遥测站实时数据 *******");
		System.out.println("原始包长度：25字节");
		System.out
				.println("原始包信息：7E7E0012345678FFFFFF378008020000130311173540057E13");
		System.out.println("中心站地址：255");
		System.out.println("水文特征码：00，水文测站编码：12345678");
		System.out.println("密码：FFFF");
		System.out.println("功能码：37");
		System.out.println("流水号：0，发报时间：2013-03-11 17:35:40");

	}

	/**
	 * 发送应答式消息
	 */
	public void sendAck(IoSession session) {
		// session.write(message);
	}

	/**
	 * {@inheritDoc}
	 */
	public void sessionClosed(IoSession session) throws Exception {
		logger.info("关闭会话  " + session.getRemoteAddress());
		// HydroServer.getInstance().getSessionManager().removeSession(session);
	}

	/**
	 * 当session创建连接后，需添加到一个客户端中
	 */
	public void sessionCreated(IoSession session) throws Exception {
		logger.info("创建会话  " + session.getRemoteAddress());
		String log = "创建会话  " + session.getRemoteAddress();
		// HydroServer.getInstance().getSessionManager().createSession(session);
	}

	/**
	 * {@inheritDoc} 在此对空闲的session进行关闭
	 */
	public void sessionIdle(IoSession session, IdleStatus status)
			throws Exception {
		// logger.info("空闲会话  " + session.getRemoteAddress());
		// HydroServer.getInstance().getSessionManager().IdleSession(session);
	}

	/**
	 * {@inheritDoc}
	 */
	public void sessionOpened(IoSession session) throws Exception {
		// logger.info("Session Opened...");

	}

	public void exceptionCaught(IoSession session, Throwable cause)
			throws Exception {

		session.close(true);

		// logger.info("会话出现异常  " + session.getRemoteAddress() + " \t"
		// + cause.getMessage());
		// cause.printStackTrace();
		// IoSessionLogger sessionLogger = IoSessionLogger.getLogger(session,
		// logger);
		logger.info(
				"会话出现异常  " + session.getRemoteAddress() + " \t"
						+ cause.getMessage(), cause);

	}

	/**
	 * 消息应答事件
	 */
	public void messageSent(IoSession session, Object message) throws Exception {
		// TODO Auto-generated method stub
		logger.info("应答会话  " + session.getRemoteAddress());
		// session.close(true);
	}

	public void inputClosed(IoSession arg0) throws Exception {
		// TODO Auto-generated method stub

	}
}
